/*
 * Copyright 2008, 2009, 2010 by Brian Dominy <brian@oddchange.com>
 *
 * This file is part of FreeWPC.
 *
 * FreeWPC is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * FreeWPC is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with FreeWPC; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 */

/*
 * Generic switch-activated coil driver
 * (can be used for slingshot and jet bumpers)
 * TODO - Gottlieb System 3 games will disable slingshots that
 * are firing too rapidly as a safety feature; perhaps this should
 * be added here?
 *
 * The parameters are:
 * sw - the switch number that triggers it
 * sol - the solenoid/coil that gets fired automatically
 * ontime - how long the coil is kept on
 * offtime - how long the coil is kept off after it was fired, before
 * 	polling the switch again.  This prevents constant lock-on.
 * auto - if nonzero, then automatically enable during start ball.
 */


@@class spsol
@@parameter sw
@@parameter sol
@@parameter ontime
@@parameter offtime
@@weakdefine auto 0

@@
@@file @self.sched
@@
!@self_service 8 40c


@@
@@file @class.h
@@

#ifndef __@class_DRIVER_H
#define __@class_DRIVER_H

extern __fastram__ U8 @class_enables;
extern __fastram__ U8 @class_running;

#endif /* __@class_DRIVER_H */


@@
@@file @self.h
@@

#include <@class.h>

#undef INST_MASK
#define INST_MASK (1 << @instance)

extern __fastram__ U8 @self_timer;

void @self_enable (void);
void @self_disable (void);

extern inline U8 @self_enabled_p (void)
{
	return @class_enables & INST_MASK;
}


extern inline U8 @self_running_p (void)
{
	return @class_running & INST_MASK;
}


extern inline void @self_service (void)
{
	/* If the timer is nonzero, then either the solenoid
	is ON, or it is in its forced OFF phase.  It is
	not necessary to poll the switch here. */
	if (unlikely (@self_timer))
	{
		/* Decrement the timer. */
		@self_timer--;

		/* Check for expiry.  Handle accordingly */
		if (@self_timer == 0)
		{
			if (@self_running_p ())
			{
				/* If the timer expires in the ON phase, then
				switch to the OFF phase.  Also, turn off the
				solenoid now. */
				sol_disable (@sol);
				@self_timer = @offtime;
				@class_running &= ~INST_MASK;
			}
			else
			{
				/* When the timer expires in the OFF phase, poll the
				switch to see that it cleared correctly.  If it hasn't,
				extend the off phase again and continue to do so until
				the switch is cleared. */
				if (rt_switch_poll (@sw))
				{
					@self_timer = @offtime;
				}
			}
		}
	}
	else if (@self_enabled_p () && rt_switch_poll (@sw))
	{
		/* When the solenoid is not running, poll its switch
		and see if it should be started. */
		sol_enable (@sol);
		@class_running |= INST_MASK;
		@self_timer = @ontime;
	}
}


@@
@@file @self.c
@@

#include <freewpc.h>
#include <@self.h>

/** We keep two bitarrays to track all special solenoids.
 * The 'enables' are software controlled and allow the
 * solenoids to be disabled.  The 'running' entries say
 * for each coil whether it is in its on or off phase. */

@@classvar __fastram__ U8 @class_enables;
@@classvar __fastram__ U8 @class_running;

/* The time until the current phase expires */
__fastram__ U8 @self_timer;

/* Enable the device */
void @self_enable (void)
{
	@class_enables |= (1 << @instance);
}

/* Disable the device */
void @self_disable (void)
{
	@class_enables &= ~(1 << @instance);
}

/* Initialize the device */
CALLSET_ENTRY (@self, init)
{
	@self_timer = 0;
	@self_disable ();
}

/* Enable the device at start ball */
CALLSET_ENTRY (@self, start_ball)
{
#if @auto
	@self_enable ();
#endif
}

/* Disable the device at end ball/stop game/tilt */
CALLSET_ENTRY (@self, end_ball, stop_game, tilt)
{
	@self_disable ();
}

/* vim: set filetype=c: */
